#include "UniqueStr.hpp"
#include "ThreadPool.hpp"
#include "MsgPool.hpp"

class Sever{
  public:
    Sever(){
      log_svr.Init();
      chat_svr.Init();
      if(!log_svr.Create() || !chat_svr.Create()){
        exit(0);
      }
      if(!log_svr.Bind() || !chat_svr.Bind()){
        exit(1);
      }
      if(!task_pool.PoolInit()){
        exit(2);
      }
      if(!log_svr.Listen()){
        exit(3);
      }
    }

    ~Sever(){
      task_pool.Quit();
    }

    void Start(){
      LOG("CharServer start success");
      while(1){
        TcpSocket* cli = new TcpSocket;
        sockaddr_in* cli_addr = new sockaddr_in;
        if(!log_svr.Accept(*cli, *cli_addr)){
          continue;
        }
        LOG("Remote have connect ip:%s, port:%u.", inet_ntoa(cli_addr->sin_addr), ntohs(cli_addr->sin_port));
        delete cli_addr;
        LogRegTask* log_reg_task = new LogRegTask(cli, &u_manager);
        task_pool.PushTask(log_reg_task); 
      }
    } 

    void Init(){
      for(int i = 0; i < MAX_THREAD_SIZE; ++i){
        int ret = pthread_create(&pro_pid[i], nullptr, RecvMassege, this);
        IsError(ret, FATAL);
        ret = pthread_create(&con_pid[i], nullptr, BroadcastMassege, this);
        IsError(ret, FATAL);
      }
    }

    static void* RecvMassege(void* arg){
      Sever* svr = (Sever*)arg;
      pthread_detach(pthread_self());
      while(1){
        sockaddr_in cli_addr;
        std::string recv_buf;
        svr->chat_svr.Recv(recv_buf, cli_addr);
        UniqueStr<Message> cli_msg(new Message);
        cli_msg->Deserialize(recv_buf);
        if(cli_msg->GetStatus() == WAITONLINE){
          ID_Type cli_id = cli_msg->GetID();
          if(svr->u_manager.HaveRegister(cli_id)){
            svr->u_manager.Online(cli_id, cli_addr);
            cli_msg->AlterInfo(svr->u_manager.GetAttr(cli_id, NICKNAME), NICKNAME);
            cli_msg->AlterInfo(svr->u_manager.GetAttr(cli_id, SCHOOL), SCHOOL);
            cli_msg->AlterInfo("", MESSAGE);
            cli_msg->SetStatus(ONLINE);
            cli_msg->Serialize(recv_buf);
            svr->chat_svr.Send(recv_buf, cli_addr);
          }
          else{
            LOG("非法消息%s", inet_ntoa(cli_addr.sin_addr));
            continue;
          }
        }
        else if(cli_msg->GetStatus() == OUTLINE){
          cli_msg->SetStatus(OUTLINE);
          svr->chat_svr.Send(recv_buf, svr->u_manager.GetUserAddr(cli_msg->GetID()));
          svr->u_manager.LogOut(cli_msg->GetID());
        }
        svr->msg_pool.PushMsg(recv_buf);
        LOG("%lld: %s",cli_msg->GetID(), user_status[cli_msg->GetStatus()]);
      }
      return nullptr;
    }

    static void* BroadcastMassege(void* arg){
      Sever* svr = (Sever*)arg;
      pthread_detach(pthread_self());
      while(1){
        std::string send_buf;
        svr->msg_pool.PopMsg(send_buf);
        for(const auto& u : svr->u_manager.GetOnlineUser()){
          svr->chat_svr.Send(send_buf, u.second);
        }
      }
    }
  private:
    bool IsExit;
    TcpSocket log_svr;
    UdpSocket chat_svr;
    ThreadPool task_pool;
    MsgPool msg_pool;
    UserManager u_manager;
    pthread_t pro_pid[MAX_THREAD_SIZE];
    pthread_t con_pid[MAX_THREAD_SIZE];
};
